@use 'sass:list';
@use 'sass:meta';
@use 'sass:string';
@use 'sass:selector';

@function create-css-variables-chain($elementOrState) {
    @if $elementOrState {
        $result: '';
        @each $elementOrStateItem in $elementOrState {
            $result: '#{$result}--#{$elementOrStateItem}';
        }

        @return $result;
    }

    @return '';
}

@function create-css-variables-fallback($fallbackValue, $defaultFallbackValue) {
    @if $fallbackValue {
        @if meta.type-of($fallbackValue) == 'string' {
            @return ', #{$fallbackValue}';
        } @else {
            @return ', #{$defaultFallbackValue}';
        }
    }

    @return '';
}

@function _border-property-side(
    $component,
    $side,
    $property,
    $elementOrState: null,
    $fallback: false
) {
    $componentChain: create-css-variables-chain($component);
    $elementOrStateChain: create-css-variables-chain($elementOrState);
    $fallbackSuffix: create-css-variables-fallback(
        $fallback,
        'var(#{$componentChain}--border-#{$side}-#{$property})'
    );

    @return var(
        #{$componentChain}#{$elementOrStateChain}--border-#{$side}-#{$property}#{$fallbackSuffix}
    );
}

@function _border-property($component, $property, $elementOrState: null, $fallback: false) {
    $componentChain: create-css-variables-chain($component);
    $elementOrStateChain: create-css-variables-chain($elementOrState);

    $border-top-fallback: $fallback;
    $border-right-fallback: $fallback;
    $border-bottom-fallback: $fallback;
    $border-left-fallback: $fallback;
    @if meta.type-of($fallback) == 'list' {
        $border-top-fallback: list.nth($fallback, 1);
        $border-right-fallback: list.nth($fallback, 2);
        $border-bottom-fallback: list.nth($fallback, 3);
        $border-left-fallback: list.nth($fallback, 4);
    }

    @return var(
        #{$componentChain}#{$elementOrStateChain}--border-#{$property},
        _border-property-side($component, 'top', $property, $elementOrState, $border-top-fallback)
            _border-property-side(
                $component,
                'right',
                $property,
                $elementOrState,
                $border-right-fallback
            )
            _border-property-side(
                $component,
                'bottom',
                $property,
                $elementOrState,
                $border-bottom-fallback
            )
            _border-property-side(
                $component,
                'left',
                $property,
                $elementOrState,
                $border-left-fallback
            )
    );
}

@function _border-radius-part($component, $part, $elementOrState: null, $fallback: false) {
    $componentChain: create-css-variables-chain($component);
    $elementOrStateChain: create-css-variables-chain($elementOrState);
    $fallbackSuffix: create-css-variables-fallback(
        $fallback,
        'var(#{$componentChain}--border-#{$part}-radius)'
    );

    @return var(
        #{$componentChain}#{$elementOrStateChain}--border-#{$part}-radius#{$fallbackSuffix}
    );
}

@function _color-property-part(
    $component,
    $property,
    $part,
    $elementOrState: null,
    $fallback: false
) {
    $componentChain: create-css-variables-chain($component);
    $elementOrStateChain: create-css-variables-chain($elementOrState);
    $fallbackSuffix: create-css-variables-fallback(
        $fallback,
        'var(#{$componentChain}--#{$property}-#{$part})'
    );

    @return var(#{$componentChain}#{$elementOrStateChain}--#{$property}-#{$part}#{$fallbackSuffix});
}

@function _color-property($component, $property, $elementOrState: null, $fallback: false) {
    $componentChain: create-css-variables-chain($component);
    $elementOrStateChain: create-css-variables-chain($elementOrState);

    $color-h-fallback: $fallback;
    $color-s-fallback: $fallback;
    $color-l-fallback: $fallback;
    $color-a-fallback: $fallback;

    @if meta.type-of($fallback) == 'list' {
        $color-h-fallback: list.nth($fallback, 1);
        $color-s-fallback: list.nth($fallback, 2);
        $color-l-fallback: list.nth($fallback, 3);
        $color-a-fallback: list.nth($fallback, 4);
    }

    @return var(
        #{$componentChain}#{$elementOrStateChain}--#{$property},
        hsla(
            _color-property-part($component, $property, 'h', $elementOrState, $color-h-fallback)
                _color-property-part($component, $property, 's', $elementOrState, $color-s-fallback)
                _color-property-part($component, $property, 'l', $elementOrState, $color-l-fallback) /
                _color-property-part($component, $property, 'a', $elementOrState, $color-a-fallback)
        )
    );
}

@function _spacing-property-side(
    $component,
    $property,
    $side,
    $elementOrState: null,
    $fallback: false
) {
    $componentChain: create-css-variables-chain($component);
    $elementOrStateChain: create-css-variables-chain($elementOrState);
    $fallbackSuffix: create-css-variables-fallback(
        $fallback,
        'var(#{$componentChain}--#{$property}-#{$side})'
    );

    @return var(#{$componentChain}#{$elementOrStateChain}--#{$property}-#{$side}#{$fallbackSuffix});
}

@function _spacing-property($component, $property, $elementOrState: null, $fallback: false) {
    $componentChain: create-css-variables-chain($component);
    $elementOrStateChain: create-css-variables-chain($elementOrState);

    @return var(
        #{$componentChain}#{$elementOrStateChain}--#{$property},
        _spacing-property-side($component, $property, 'top', $elementOrState, $fallback)
            _spacing-property-side($component, $property, 'right', $elementOrState, $fallback)
            _spacing-property-side($component, $property, 'bottom', $elementOrState, $fallback)
            _spacing-property-side($component, $property, 'left', $elementOrState, $fallback)
    );
}

@function _generic-component-property(
    $component,
    $property,
    $elementOrState: null,
    $fallback: false
) {
    $componentChain: create-css-variables-chain($component);
    $elementOrStateChain: create-css-variables-chain($elementOrState);
    $fallbackSuffix: create-css-variables-fallback(
        $fallback,
        'var(#{$componentChain}--#{$property})'
    );

    @return var(#{$componentChain}#{$elementOrStateChain}--#{$property}#{$fallbackSuffix});
}

@function _generic-property($property, $fallback: false) {
    $fallbackSuffix: create-css-variables-fallback($fallback, 'var(--#{$property})');

    @return var(--#{$property}#{$fallbackSuffix});
}

@function animation-property($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'animation', $elementOrState, $fallback);
}

@function animation-duration($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property(
        $component,
        'animation-duration',
        $elementOrState,
        $fallback
    );
}

@function animation-timing-function($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property(
        $component,
        'animation-timing-function',
        $elementOrState,
        $fallback
    );
}

@function border-width($component, $elementOrState: null, $fallback: false) {
    @return _border-property($component, 'width', $elementOrState, $fallback);
}

@function border-top-width($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'top', 'width', $elementOrState, $fallback);
}

@function border-right-width($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'right', 'width', $elementOrState, $fallback);
}

@function border-bottom-width($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'bottom', 'width', $elementOrState, $fallback);
}

@function border-left-width($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'left', 'width', $elementOrState, $fallback);
}

@function border-style($component, $elementOrState: null, $fallback: false) {
    @return _border-property($component, 'style', $elementOrState, $fallback);
}

@function border-top-style($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'top', 'style', $elementOrState, $fallback);
}

@function border-right-style($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'right', 'style', $elementOrState, $fallback);
}

@function border-bottom-style($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'bottom', 'style', $elementOrState, $fallback);
}

@function border-left-style($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'left', 'style', $elementOrState, $fallback);
}

@function border-color($component, $elementOrState: null, $fallback: false) {
    @return _border-property($component, 'color', $elementOrState, $fallback);
}

@function border-top-color($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'top', 'color', $elementOrState, $fallback);
}

@function border-right-color($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'right', 'color', $elementOrState, $fallback);
}

@function border-bottom-color($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'bottom', 'color', $elementOrState, $fallback);
}

@function border-left-color($component, $elementOrState: null, $fallback: false) {
    @return _border-property-side($component, 'left', 'color', $elementOrState, $fallback);
}

@function border-radius($component, $elementOrState: null, $fallback: false) {
    $componentChain: create-css-variables-chain($component);
    $elementOrStateChain: create-css-variables-chain($elementOrState);

    $border-top-left-radius-fallback: $fallback;
    $border-top-right-radius-fallback: $fallback;
    $border-bottom-right-radius-fallback: $fallback;
    $border-bottom-left-radius-fallback: $fallback;
    @if meta.type-of($fallback) == 'list' {
        $border-top-left-radius-fallback: list.nth($fallback, 1);
        $border-top-right-radius-fallback: list.nth($fallback, 2);
        $border-bottom-right-radius-fallback: list.nth($fallback, 3);
        $border-bottom-left-radius-fallback: list.nth($fallback, 4);
    }

    @return var(
        #{$componentChain}#{$elementOrStateChain}--border-radius,
        border-top-left-radius($component, $elementOrState, $border-top-left-radius-fallback)
            border-top-right-radius($component, $elementOrState, $border-top-right-radius-fallback)
            border-bottom-right-radius(
                $component,
                $elementOrState,
                $border-bottom-right-radius-fallback
            )
            border-bottom-left-radius(
                $component,
                $elementOrState,
                $border-bottom-left-radius-fallback
            )
    );
}

@function border-top-left-radius($component, $elementOrState: null, $fallback: false) {
    @return _border-radius-part($component, 'top-left', $elementOrState, $fallback);
}

@function border-top-right-radius($component, $elementOrState: null, $fallback: false) {
    @return _border-radius-part($component, 'top-right', $elementOrState, $fallback);
}

@function border-bottom-right-radius($component, $elementOrState: null, $fallback: false) {
    @return _border-radius-part($component, 'bottom-right', $elementOrState, $fallback);
}

@function border-bottom-left-radius($component, $elementOrState: null, $fallback: false) {
    @return _border-radius-part($component, 'bottom-left', $elementOrState, $fallback);
}

@function _box-shadow-part($component, $part, $elementOrState: null, $fallback: false) {
    $componentChain: create-css-variables-chain($component);
    $elementOrStateChain: create-css-variables-chain($elementOrState);
    $fallbackSuffix: create-css-variables-fallback(
        $fallback,
        'var(#{$componentChain}--box-shadow-#{$part})'
    );

    @return var(#{$componentChain}#{$elementOrStateChain}--box-shadow-#{$part}#{$fallbackSuffix});
}

@function box-shadow-offset-x($component, $elementOrState: null, $fallback: false) {
    @return _box-shadow-part($component, 'offset-x', $elementOrState, $fallback);
}

@function box-shadow-offset-y($component, $elementOrState: null, $fallback: false) {
    @return _box-shadow-part($component, 'offset-y', $elementOrState, $fallback);
}

@function box-shadow-blur-radius($component, $elementOrState: null, $fallback: false) {
    @return _box-shadow-part($component, 'blur-radius', $elementOrState, $fallback);
}

@function box-shadow-spread-radius($component, $elementOrState: null, $fallback: false) {
    @return _box-shadow-part($component, 'spread-radius', $elementOrState, $fallback);
}

@function box-shadow-color($component, $elementOrState: null, $fallback: false) {
    @return _box-shadow-part($component, 'color', $elementOrState, $fallback);
}

@function box-shadow($component, $elementOrState: null, $fallback: false) {
    $componentChain: create-css-variables-chain($component);
    $elementOrStateChain: create-css-variables-chain($elementOrState);

    $box-shadow-offset-x-fallback: $fallback;
    $box-shadow-offset-y-fallback: $fallback;
    $box-shadow-blur-radius-fallback: $fallback;
    $box-shadow-spread-radius-fallback: $fallback;
    $box-shadow-color-fallback: $fallback;
    @if meta.type-of($fallback) == 'list' {
        $box-shadow-offset-x-fallback: list.nth($fallback, 1);
        $box-shadow-offset-y-fallback: list.nth($fallback, 2);
        $box-shadow-blur-radius-fallback: list.nth($fallback, 3);
        $box-shadow-spread-radius-fallback: list.nth($fallback, 4);
        $box-shadow-color-fallback: list.nth($fallback, 5);
    }

    @return var(
        #{$componentChain}#{$elementOrStateChain}--box-shadow,
        box-shadow-offset-x($component, $elementOrState, $box-shadow-offset-x-fallback)
            box-shadow-offset-y($component, $elementOrState, $box-shadow-offset-y-fallback)
            box-shadow-blur-radius($component, $elementOrState, $box-shadow-blur-radius-fallback)
            box-shadow-spread-radius(
                $component,
                $elementOrState,
                $box-shadow-spread-radius-fallback
            )
            box-shadow-color($component, $elementOrState, $box-shadow-color-fallback)
    );
}

@function background($component, $elementOrState: null, $fallback: false) {
    @return _color-property($component, 'background', $elementOrState, $fallback);
}

@function background-h($component, $elementOrState: null, $fallback: false) {
    @return _color-property-part($component, 'background', 'h', $elementOrState, $fallback);
}

@function background-s($component, $elementOrState: null, $fallback: false) {
    @return _color-property-part($component, 'background', 's', $elementOrState, $fallback);
}

@function background-l($component, $elementOrState: null, $fallback: false) {
    @return _color-property-part($component, 'background', 'l', $elementOrState, $fallback);
}

@function background-a($component, $elementOrState: null, $fallback: false) {
    @return _color-property-part($component, 'background', 'a', $elementOrState, $fallback);
}

@function color($component, $elementOrState: null, $fallback: false) {
    @return _color-property($component, 'color', $elementOrState, $fallback);
}

@function color-h($component, $elementOrState: null, $fallback: false) {
    @return _color-property-part($component, 'color', 'h', $elementOrState, $fallback);
}

@function color-s($component, $elementOrState: null, $fallback: false) {
    @return _color-property-part($component, 'color', 's', $elementOrState, $fallback);
}

@function color-l($component, $elementOrState: null, $fallback: false) {
    @return _color-property-part($component, 'color', 'l', $elementOrState, $fallback);
}

@function color-a($component, $elementOrState: null, $fallback: false) {
    @return _color-property-part($component, 'color', 'a', $elementOrState, $fallback);
}

@function font-family($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'font-family', $elementOrState, $fallback);
}

@function font-size($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'font-size', $elementOrState, $fallback);
}

@function font-style($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'font-style', $elementOrState, $fallback);
}

@function font-weight($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'font-weight', $elementOrState, $fallback);
}

@function line-height($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'line-height', $elementOrState, $fallback);
}

@function margin($component, $elementOrState: null, $fallback: false) {
    @return _spacing-property($component, 'margin', $elementOrState, $fallback);
}

@function margin-top($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'margin-top', $elementOrState, $fallback);
}

@function margin-right($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'margin-right', $elementOrState, $fallback);
}

@function margin-bottom($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'margin-bottom', $elementOrState, $fallback);
}

@function margin-left($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'margin-left', $elementOrState, $fallback);
}

@function opacity($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'opacity', $elementOrState, $fallback);
}

@function padding($component, $elementOrState: null, $fallback: false) {
    @return _spacing-property($component, 'padding', $elementOrState, $fallback);
}

@function padding-top($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'padding-top', $elementOrState, $fallback);
}

@function padding-right($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'padding-right', $elementOrState, $fallback);
}

@function padding-bottom($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'padding-bottom', $elementOrState, $fallback);
}

@function padding-left($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'padding-left', $elementOrState, $fallback);
}

@function spacing($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'spacing', $elementOrState, $fallback);
}

@function width($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'width', $elementOrState, $fallback);
}

@function min-width($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'min-width', $elementOrState, $fallback);
}

@function max-width($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'max-width', $elementOrState, $fallback);
}

@function height($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'height', $elementOrState, $fallback);
}

@function min-height($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'min-height', $elementOrState, $fallback);
}

@function max-height($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'max-height', $elementOrState, $fallback);
}

@function size($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'size', $elementOrState, $fallback);
}

@function transition-property($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property(
        $component,
        'transition-property',
        $elementOrState,
        $fallback
    );
}

@function transition-duration($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property(
        $component,
        'transition-duration',
        $elementOrState,
        $fallback
    );
}

@function transition-timing-function($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property(
        $component,
        'transition-timing-function',
        $elementOrState,
        $fallback
    );
}

@function text-align($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'text-align', $elementOrState, $fallback);
}

@function text-decoration($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'text-decoration', $elementOrState, $fallback);
}

@function text-transform($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'text-transform', $elementOrState, $fallback);
}

@function top($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'top', $elementOrState, $fallback);
}

@function right($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'right', $elementOrState, $fallback);
}

@function bottom($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'bottom', $elementOrState, $fallback);
}

@function left($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'left', $elementOrState, $fallback);
}

@function z-index($component, $elementOrState: null, $fallback: false) {
    @return _generic-component-property($component, 'z-index', $elementOrState, $fallback);
}

@function generic($componentOrProperty, $property: null, $elementOrState: null, $fallback: false) {
    @if not $property {
        @return _generic-property($componentOrProperty, $fallback);
    }

    @return _generic-component-property(
        $componentOrProperty,
        $property,
        $elementOrState,
        $fallback
    );
}
